package com.purang.andfix2;

import android.content.Context;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Enumeration;

import dalvik.system.DexFile;

public class DexManager {
    private Context mContext;
    private static final DexManager ourInstance = new DexManager();

    public static DexManager getInstance() {
        return ourInstance;
    }

    static {
        System.loadLibrary("native-lib");
    }

    private DexManager() {
    }

    public void setContext(Context context) {
        this.mContext = context;
    }

    public void fix(File file) {
        try {
            // 加载dex文件到 app缓存目录下(data/data/packageName/cache/)
            DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
                    new File(mContext.getCacheDir(), "opt").getAbsolutePath(), Context.MODE_PRIVATE);
            Enumeration<String> entries = dexFile.entries();//拿到dex文件中所有的包
            while (entries.hasMoreElements()) {
                String element = entries.nextElement(); // 拿到每个包中，不同的类名
//                Class<?> clazz = Class.forName(element);// 这种反射方式加载类，只能加载在手机上已经安装的APP中的类.
                // 加载类
                Class fixedClass = dexFile.loadClass(element, mContext.getClassLoader());
                if (null != fixedClass) {
                    fixClass(fixedClass);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void fixClass(Class fixedClass) {
        Method[] methods = fixedClass.getDeclaredMethods();
        for (Method fixedMethod : methods) {
            ReplaceMethod methodAnnotation = fixedMethod.getAnnotation(ReplaceMethod.class);
            if (null == methodAnnotation) {
                continue;
            }
            String fixedClassName = methodAnnotation.className();
            String fixedMethodName = methodAnnotation.methodName();
            // 根据 fixedMethodName 找到 bugMethodName
            try {
                Class<?> bugClass = Class.forName(fixedClassName);
                if (null == bugClass) {
                    continue;
                }
                Method bugMethod = bugClass.getDeclaredMethod(fixedMethodName, fixedMethod.getParameterTypes());
                replace(fixedMethod, bugMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private native void replace(Method fixedMethod, Method bugMethod);
}
